/*
 * ext2.c - Mokri
 * Contributors: xMine
 * (Any new Contributor may add himself/herself to the list)
 * Changes and fixes to be made: 
 * Source: http://www.nongnu.org/ext2-doc/ext2.html
 */

#include "ext2.h"

FILE *ext_device = NULL;
ext_superblock_t ext_superblock;
ext_group_desc_t ext_group_desc;
ext_inode_table_t ext_dir_inode;

ext_cache_t ext_cache[EXT_CACHE_BLOCKS];
uint32_t cachenum; // Enthält die Anzahl der momentan gecachten Blöcke
ext_linked_list_dir_t *ext_directorys[1024];
uint32_t ext_dir_num = 0; // Anzahl von Directory Tables
char ext_drv; // Treibernummer vom VFS
lock_t ext_lock;

/* Liest den Superblock des ext2 Dateisystems aus.
 * Parameter: Keine
 * Rückgaben: TRUE bei Erfolg, bei Fehlern (zB wenn das Gerät kein ext2 Gerät ist) FALSE
 */
BOOL ext_read_superblock () {
	char superblock[1024]; // Der Superblock ist 1kB groß
	
	// Da es noch keinen Superblock gibt im RAM, müssen wir die Daten per Hand auslesen
	fseek (ext_device, 1024, SEEK_SET); 		// Der Superblock startet bei 1024Bytes
	fread (superblock, 512, 1, ext_device); 	// Ersten Sektor lesen
	fread (superblock+512, 512, 1, ext_device); 	// Zweiten Sektor lesen
	
	memcpy (&ext_superblock, superblock, sizeof (ext_superblock_t)); // Superblock rüber kopieren
	
	#ifdef EXT_READ_SUPERBLOCK_DEBUG
	printf ("ext_read_superblock:\n");
	printf ("Ext2 Dateisystem: ");
	#endif
	
	if (ext_superblock.s_magic != EXT2_SUPER_MAGIC) {
		#ifdef EXT_READ_SUPERBLOCK_DEBUG
		print ("NEIN\n");
		#endif
		return FALSE;
	}
	
	#ifdef EXT_READ_SUPERBLOCK_DEBUG
	print ("JA\n");
	
	printf ("Versionsnummer: %c\n", (char)'0'+ext_superblock.s_rev_level);
	printf ("iNodes: %i freie: %i\n", (int)ext_superblock.s_inodes_count, (int)ext_superblock.s_free_inodes_count);
	printf ("Blocks: %i freie: %i\n", (int)ext_superblock.s_blocks_count, (int)ext_superblock.s_free_blocks_count);
	printf ("Erster iNode in Block Nr. %i\n", (int)ext_superblock.s_first_ino);
	printf ("Groesse eines iNode: %i\n", (int)ext_superblock.s_inode_size);
	printf ("===\n");
	#endif
	
	return TRUE;
}

/* Kopiert einen Block in den Cache
 * Parameter: Zeiger auf einen Superblock, Block nr, Zeiger auf einen Block
 * Rückgaben: Keine
 */
void ext_cache_block (ext_superblock_t *sb, uint32_t block, char *data) {
	#ifdef EXT_CACHE_ENABLED
	int i;

	ext_remove_cache_block (0x0, block); // Ggf. den Block ausm Speicher werfen
	
	char *cache = (char*)malloc (ext_get_block_size(sb)); // Cache erstellen
	memcpy (cache, data, ext_get_block_size(sb)); // Und rüber kopieren
	
	cachenum++;
	// Wenn der Cache voll ist geben wir den ältesten Block wieder frei
	if (cachenum >= EXT_CACHE_BLOCKS) {
		free (ext_cache[EXT_CACHE_BLOCKS-1].cache);
		cachenum--;
	}
	
	for (i=EXT_CACHE_BLOCKS-2; i>=0; i--) { // Einträge nach hinten verschieben
		memcpy (ext_cache+sizeof(ext_cache_t)*(i+1), ext_cache+sizeof(ext_cache_t)*i, sizeof(ext_cache_t));
	}
	// ext_cache[0] ist nun Frei
	ext_cache[0].cache = cache;
	ext_cache[0].blocknum = block;
	ext_cache[0].device = 0x0;
	#endif
}

/* Sucht einen Block im Cache
 * Parameter: Device ID, Block nr
 * Rückgaben: Wenn der Block gefunden wurde einen Zeiger auf ihn, ansonsten NULL
 */
char *ext_get_block_cache (char device, uint32_t block) {
	#ifdef EXT_CACHE_ENABLED
	int i;
	for (i=0; i < cachenum; i++) {
		if (ext_cache[i].device == device && ext_cache[i].blocknum == block) {
			return ext_cache[i].cache;
		}
	}
	
	#endif
	
	return NULL;
}

/* Löscht einen Block aus dem Cache
 * Parameter: Device ID, Block nr
 * RÜckgaben: Keine
 */
void ext_remove_cache_block (char device, uint32_t block) {
	#ifdef EXT_CACHE_ENABLED
	int i;
	for (i=0; i < cachenum; i++) {
		if (ext_cache[i].device == device && ext_cache[i].blocknum == block) {
			break;
		}
	}
	
	if (i == cachenum) { // Wenn der Block nicht gefunden wurde...
		return;
	}
	
	memcpy (ext_cache+sizeof(ext_cache_t)*i, ext_cache+sizeof(ext_cache_t)*(i+1), (cachenum-i)*sizeof(ext_cache_t));
	cachenum--;
	
	#endif
}

/* Gibt die Größe eines Blocks zurück
 * Parameter: Pointer auf einen Superblock
 * Rückgaben: Blockgröße in Bytes
 */
inline uint32_t ext_get_block_size (ext_superblock_t *sb) {
	return 1024 << sb->s_log_block_size;
}

/* Liest einen Block
 * Parameter: Pointer auf eine Superblock Struktur, Blocknummer, Puffer
 * Rückgaben: Bei Erfolg TRUE, ansonsten FALSE
 */
BOOL ext_read_block (ext_superblock_t *sb, uint32_t block, void *buffer) {
	if (block > sb->s_blocks_count) { // Gibt es diesen Block überhaupt?
		return FALSE;
	}
	
	uint32_t sektoren = ext_get_block_size (sb) / 512; // Anzahl zu lesender Sektoren
	uint32_t pos = block*ext_get_block_size (sb); // Absolute Position vom Anfang des Geräts
	uint32_t i;
	char *cache = ext_get_block_cache (0x0, block);
	
	if (cache != NULL) { // Liegt der Block schon im Cache?
		memcpy (buffer, cache, ext_get_block_size (sb));
		return TRUE;
	}
	
	lock_wait (&ext_lock);
	ext_lock = LOCKED;
	
	if (fseek (ext_device, (int)pos, SEEK_SET) != 0) {
		ext_lock = UNLOCKED;
		return FALSE;
	}
	
	for (i=0; i<sektoren; i++, pos += 512) {
		
		if (fread (buffer+(i*512), 512, 1, ext_device) != 512) {
			ext_lock = UNLOCKED;
			return FALSE;
		}
	}
	
	// Hier noch den Block cachen
	ext_cache_block (sb, block, buffer);
	
	//print ("OK\n");
	ext_lock = UNLOCKED;
	return TRUE;
}

/* Schreibt einen Block
 * Parameter: Pointer auf eine Superblock Struktur, Blocknummer, Puffer
 * Rückgaben: Bei Erfolg TRUE, ansonsten FALSE
 */
BOOL ext_write_block (ext_superblock_t *sb, uint32_t block, void *buffer) {
	if (block > sb->s_blocks_count) { // Gibt es diesen Block überhaupt?
		return FALSE;
	}
	
	uint32_t sektoren = ext_get_block_size (sb) / 512; // Anzahl zu lesender Sektoren
	uint32_t pos = block*ext_get_block_size (sb); // Absolute Position vom Anfang des Geräts
	uint32_t i;
	
	//ext_remove_cache_block (0x0, block); // Block ausm Cache werfen
	
	lock_wait (&ext_lock);
	ext_lock = LOCKED;
	
	if (fseek (ext_device, (int)pos, SEEK_SET) != 0) {
		ext_lock = UNLOCKED;
		return FALSE;
	}
	
	for (i=0; i<sektoren; i++, pos += 512) {
		
		if (fwrite (buffer+(i*512), 512, 1, ext_device) != 512) {
			ext_lock = UNLOCKED;
			return FALSE;
		}
	}
	
	ext_cache_block (sb, block, buffer); // Und den Block wieder in den Cache stecken
	
	ext_lock = UNLOCKED;
	return TRUE;
}

/* Liest die Block Gruppen Deskriptor Tabelle aus
 * Parameter: Pointer zu einer Superblock Struktur, Pointer auf einer BGDT Struktur
 * Rückgaben: Bei Erfolg TRUE, ansonsten FALSE
 */
BOOL ext_read_block_group_desc (ext_superblock_t *sb, ext_group_desc_t *gd) {
	uint32_t block = 2;
	
	char *buffer = (char*) malloc (ext_get_block_size (sb));
	
	if (!ext_read_block (sb, block, buffer)) { // Tabelle auslesen
		//print ("FAIL\n");
		free (buffer);
		return FALSE;
	}
	
	memcpy (gd, buffer, sizeof (ext_group_desc_t));
	
	free (buffer);
	
	return TRUE;
}

/* Sucht einen freien Block und markiert diesen als benutzt
 * Parameter: Pointer auf einen Superblock, Pointer auf eine BGDT
 * Rückgaben: Bei Erfolg einen Block, ansonsten 0
 */
uint32_t ext_find_free_block (ext_superblock_t *sb, ext_group_desc_t *gd) {
	uint32_t blocksize = ext_get_block_size (sb);
	char *bitmap = (char*)malloc (blocksize);
	
	if (!ext_read_block (sb, gd->bg_block_bitmap, bitmap)) { // Bitmap Block lesen
		free (bitmap);
		return FALSE;
	}
	
	int i, j;
	
	for (i=0; i<blocksize; i++) {
		if (bitmap[i] == 0xFF) {
			continue;
		}
		
		for (j=0; j<8; j++) {
			if ( (bitmap[i] & (1 << j)) == 0) {
				break;
			}
		}
		
		if (j<8) {
			break;
		}
	}
	
	uint32_t block = i*8+j;
	if (block > sb->s_blocks_count) { // Gibt es diesen Block überhaupt?
		block = 0;
	}
	
	free (bitmap);
	
	ext_change_block_status (sb, gd, block, TRUE);
	
	return block;
}

/* Ändert den Status eines Blocks in einer Block Group
 * Parameter: Pointer auf eine Superblock Struktur, Pointer auf eine BGDT Struktur, Block, Status (1 = Benutzt, 0 = Frei)
 * Rückgaben: Bei Erfolg TRUE, ansonsten FALSE
 */
BOOL ext_change_block_status (ext_superblock_t *sb, ext_group_desc_t *gd, uint32_t block, uint8_t status) {
	uint32_t blocksize = ext_get_block_size (sb);
	
	char *bitmap = (char*)malloc (blocksize);
	
	if (!ext_read_block (sb, gd->bg_block_bitmap, bitmap)) { // Bitmap Block lesen
		free (bitmap);
		return FALSE;
	}
	
	size_t index = (size_t) block / 8;
	
	// Das kann man bestimmt auch ohne if-Anweisung schöner lösen
	if (status) { // Setzen
		bitmap[index] |= (1 << (((size_t) block) & (8 - 1)));
		sb->s_free_blocks_count--;
	} else { // Löschen
		bitmap[index] &= ~(1 << (((size_t) block) & (8 - 1)));
		sb->s_free_blocks_count++;
	}
	
	if (!ext_write_block (sb, gd->bg_block_bitmap, bitmap)) { // Bitmap Block wieder schreiben
		free (bitmap);
		return FALSE;
	}
	
	free (bitmap);
	
	return TRUE;
}

/* Ändert den Status eines iNode in einer Block Group
 * Parameter: Pointer auf eine Superblock Struktur, Pointer auf eine BGDT Struktur, Block, Status (1 = Benutzt, 0 = Frei)
 * Rückgaben: Bei Erfolg TRUE, ansonsten FALSE
 */
BOOL ext_change_inode_status (ext_superblock_t *sb, ext_group_desc_t *gd, uint32_t inode, uint8_t status) {
	uint32_t blocksize = ext_get_block_size (sb);
	
	char *bitmap = (char*)malloc (blocksize);
	
	if (!ext_read_block (sb, gd->bg_inode_bitmap, bitmap)) { // Bitmap Block lesen
		free (bitmap);
		return FALSE;
	}
	
	size_t index = (size_t) inode / 8;
	
	// Das kann man bestimmt auch ohne if-Anweisung schöner lösen
	if (status) { // Setzen
		bitmap[index] |= (1 << (((size_t) inode) & (8 - 1)));
	} else { // Löschen
		bitmap[index] &= ~(1 << (((size_t) inode) & (8 - 1)));
	}
	
	if (!ext_write_block (sb, gd->bg_inode_bitmap, bitmap)) { // Bitmap Block wieder schreiben
		free (bitmap);
		return FALSE;
	}
	
	free (bitmap);
	
	return TRUE;
}

/* Überprüft ob ein iNode frei ist oder nicht
 * Parameter: Pointer auf eine Superblock Struktur, Pointer auf eine BGDT Struktur, Block 
 * Rückgaben: Wenn der iNode frei ist FALSE, wenn er in Gebrauch ist TRUE
 */
BOOL ext_inode_in_use (ext_superblock_t *sb, ext_group_desc_t *gd, uint32_t inode) {
	uint32_t blocksize = ext_get_block_size (sb);
	
	uint32_t *bitmap = (uint32_t*)malloc (blocksize);
	
	if (!ext_read_block (sb, gd->bg_inode_bitmap, bitmap)) { // Bitmap Block lesen
		free (bitmap);
		return FALSE;
	}
	
	uint32_t index = inode / 32;
	if ((bitmap[index] & (1 << (inode % 32))) == 0x0) { // Sehen, ob das Bit gesetzt ist
		free (bitmap);
		return FALSE;
	}
	
	free (bitmap);
	return TRUE;
}

/* Liest einen iNode aus
 * Parameter: Pointer auf eine Superblock Struktur, Block der den iNode enthält, Index im Block, Pointer auf eine iNode Struktur
 * Rückgaben: Bei Erfolg TRUE, ansonsten FALSE
 */
BOOL ext_read_inode (ext_superblock_t *sb, ext_group_desc_t *bg, uint32_t index, ext_inode_table_t *inode) {
	char *buffer = (char*)malloc (ext_get_block_size(sb));
	
	uint32_t tables_per_block = ext_get_block_size (sb)/sb->s_inode_size;
	
	uint32_t block = bg->bg_inode_table+index/tables_per_block;
	uint32_t local = index % tables_per_block; 
	
	if (!ext_read_block (sb, block, buffer)) { // Block lesen
		free (buffer);
		return FALSE;
	}
	
	
	memcpy (inode, buffer+local*sb->s_inode_size, sizeof (ext_inode_table_t));
	
	free (buffer);
	
	return TRUE;
}

/* Aktualisiert die Daten eines iNodes
 * Parameter: Zeiger auf einen Superblock, Zeiger auf eine BGDT, iNode Index, 
 * Zeiger auf einen iNode, Blockliste, Dateigröße, Zugegriffen, beschrieben, gelöscht
 * Rückgaben: Bei Erfolg TRUE, ansonsten FALSE
 */
BOOL ext_update_inode (ext_superblock_t *sb, ext_group_desc_t *bg, uint32_t index, ext_inode_table_t *inode,
		       uint32_t *blocklist, uint32_t filesize, BOOL accessed, BOOL written, BOOL removed) {
	
	char *buffer = (char*)malloc (ext_get_block_size(sb));
	
	uint32_t tables_per_block = ext_get_block_size (sb)/sb->s_inode_size;
	
	uint32_t block = bg->bg_inode_table+index/tables_per_block;
	uint32_t local = index % tables_per_block; 
	
	if (!ext_read_block (sb, block, buffer)) { // Block lesen
		free (buffer);
		return FALSE;
	}
	
	if (written) {
		if (filesize != inode->i_size) { // Blockliste aktualisieren?
			inode->i_size = filesize;
			if (!ext_write_block_list (sb, bg, inode, blocklist)) {
				free (buffer);
				return FALSE;
			}
		}
		
		// TODO: UNIX Zeit eintragen
	}
	
	if (accessed) {
		// TODO: UNIX Zeit eintragen
	}
	
	if (removed) {
		// TODO; UNIX Zeit eintragen
	}
	
	memcpy (buffer+local*sb->s_inode_size, inode, sizeof (ext_inode_table_t));
	
	if (ext_write_block (sb, block, buffer)) { // Und das ganze wieder schreiben
		free (buffer);
		return TRUE;
	}
	
	free (buffer);
	return FALSE;
}

/* Durchsucht die Dir Tables nach einen iNode.
 * TODO: Hier funktionieren maximal 1024 iNodes, das sollte man ausbessern
 * Parameter: Gesuchter iNode, Auszuschliessender Index (wenn keiner ausgeschlossen werden soll, ist dieser Wert = 0xFFFFFFFF)
 * Rückgaben: Wenn der iNode gefunden wurde TRUE, ansonsten FALSE
 */
BOOL ext_find_inode (uint32_t inode, uint32_t exclude) {
	uint32_t i;
	
	for (i=0; i < 1024 && (i == exclude || ext_directorys[i]->inode != inode); i++);
	
	return (i < 1024);
}

/* Gibt eine Liste mit Blöcken zurück
 * Parameter: Zeiger auf einen Superblock, Zeiger auf einen iNode
 * Rückgaben: Pointer auf ein uint32_t Array, der alle Blöcke enthält
 */
uint32_t *ext_get_block_list (ext_superblock_t *sb, ext_inode_table_t *inode) {
	uint32_t blocksize = ext_get_block_size(sb);
	uint32_t bpb = blocksize / sizeof(uint32_t);
	uint32_t *blocks = (uint32_t*)calloc (inode->i_blocks-1, sizeof(uint32_t));
	uint32_t num = inode->i_blocks-1;
	
	int i;
	
	// Direkte Blöcke => Maximale Dateigröße = 12*Blockgröße
	for (i=0; i<12; i++) {
		if (inode->i_block[i] == 0) {
			break;
		}
		
		blocks[i] = inode->i_block[i];
		num--;
	}
	
	// Dies ist ein einfach indirekter Block
	if (num > 0 && inode->i_block[12] > 0) {
		uint32_t *buf1 = (uint32_t*)malloc (blocksize);
		
		if (!ext_read_block (sb, inode->i_block[12], buf1)) {
			printf ("ext_get_block_list: FEHLER: Konnte Block %u nicht lesen!\n", inode->i_block[12]);
		}
		
		int j;
		for (j=0; j<bpb; j++, i++) {
			
			if (num < 1 || buf1[j] == 0) {
				break;
			}
			
			blocks[i] = buf1[j];
			num--;
		}
		
		free (buf1);
	}
	
	// Dies ist ein zweifach indirekter Block
	if (num > 0 && inode->i_block[13] > 0) {
		uint32_t *buf1 = (uint32_t*)malloc (blocksize);
		uint32_t *buf2 = (uint32_t*)malloc (blocksize);
		
		if (!ext_read_block (sb, inode->i_block[13], buf1)) {
			printf ("ext_get_block_list: FEHLER: Konnte Block %u nicht lesen!\n", inode->i_block[12]);
		}
		
		int j, k;
		for (j=0; j<bpb; j++) {
			
			if (num < 1 || buf1[j] == 0) {
				break;
			}
			
			if (!ext_read_block (sb, buf1[j], buf2)) {
				printf ("ext_get_block_list: FEHLER: Konnte Block %u nicht lesen!\n", buf1[j]);
			}
			
			for (k=0; k<bpb; k++, i++) {
				blocks[i] = buf2[k];
				num--;
			}
		}
		
		free (buf1);
		free (buf2);
	}
	
	return blocks;
}

/* Schreibt eine Blockliste in einen iNode
 * Parameter: Zeiger auf einen Superblock, Zeiger auf eine BGDT, Zeiger auf einen iNode, Blockliste
 * Rückgaben: Bei Erfolg TRUE, ansonsten FALSE
 */
BOOL ext_write_block_list (ext_superblock_t *sb, ext_group_desc_t *bg, ext_inode_table_t *inode, uint32_t *blocks) {
	uint32_t blocksize = ext_get_block_size(sb);
	uint32_t bpb = blocksize / sizeof(uint32_t);
	uint32_t num = inode->i_blocks-1;
	
	int i;
	
	// Direkte Blöcke => Maximale Dateigröße = 12*Blockgröße
	for (i=0; i<12; i++) {
		if (blocks[i] == 0) {
			break;
		}
		
		inode->i_block[i] = blocks[i];
		num--;
	}
	
	// Dies ist ein einfach indirekter Block
	if (num > 0) {
		if (inode->i_block[12] == 0) { // Neuen Block suchen
			inode->i_block[12] = ext_find_free_block (sb, bg);
			
			if (inode->i_block[12] == 0) { // Kein Speicher mehr frei
				return FALSE;
			}
		}
		
		uint32_t *buf1 = (uint32_t*)calloc (bpb, sizeof (uint32_t));
		
		// Block mit Daten füllen
		int j;
		for (j=0; j<bpb; j++, i++) {
			
			if (num < 1) {
				break;
			}
			
			buf1[j] = blocks[i];
			num--;
		}
		
		ext_write_block (sb, inode->i_block[12], buf1); // Und raus damit
		
		free (buf1);
	}
	
	// Dies ist ein zweifach indirekter Block
	if (num > 0) {
		uint32_t *buf1 = (uint32_t*)calloc (bpb, sizeof (uint32_t));
		uint32_t *buf2 = (uint32_t*)malloc (bpb*sizeof(uint32_t));
		
		if (inode->i_block[13] == 0) { // Neuen Block suchen
			inode->i_block[13] = ext_find_free_block (sb, bg);
			
			if (inode->i_block[13] == 0) { // Kein Speicher mehr frei
				return FALSE;
			}
			
			ext_write_block (sb, inode->i_block[13], buf1); // Ggf. den Block leeren
		}
		
		// Block mit Daten füllen
		int j, k;
		for (j=0; j<bpb; j++) {
			if (num < 1) {
				break;
			}
			
			memset (buf2, 0x0, blocksize);
			
			if (buf1[j] == 0) { // Neuen Block suchen
				buf1[j] = ext_find_free_block (sb, bg);
				
				if (buf1[j] == 0) { // Kein Speicher mehr frei
					return FALSE;
				}
			}
			
			for (k=0; k<bpb; k++, i++) {
				buf2[k] = blocks[i];
				num--;
			}
			
			ext_write_block (sb, buf1[j], buf2);

		}
		
		ext_write_block (sb, inode->i_block[13], buf1); // Und raus damit
		
		free (buf1);
	}
	
	return TRUE;
}

/* Liest die Directory Tables aus und macht die Einträge dem VFS bekannt
 * Parameter: Pointer auf eine Superblock Struktur, Pointer auf eine BGDT Struktur,
 *	Pointer auf eine iNode Struktur die die Directory Tables enthält,
 *	Pointer auf eine fs_entry Struktur die als Übergeordnetes Verzeichniss fungiert
 *	Rückgaben: Bei Erfolg TRUE, ansonsten FALSE
 */
BOOL ext_read_dir_tables (ext_superblock_t *sb, ext_group_desc_t *bg, ext_inode_table_t *inode, struct fs_entry *parent) {
	char *buffer = (char*)malloc (ext_get_block_size (sb));
	uint32_t *blocklist = ext_get_block_list (sb, inode);
	int i;
	
	
	#ifdef DEBUG_READ_DIR_TABLES
	printf ("Blocke mit DirTables: %u\n", (uint32_t)inode->i_blocks);
	printf ("Blocke: ");
	
	for (i=0; i<inode->i_blocks; i++) {
		printf ("%u ", blocklist[i]);
	}
	
	print ("\n");
	#endif
	
	for (i=0; i<inode->i_blocks; i++) {
		#ifdef DEBUG_READ_DIR_TABLES
		printf ("Lese Block %u\n", blocklist[i]);
		#endif
		
		
		if (blocklist[i] == 0) {
			break;
		}
		
		
		ext_read_block (sb, blocklist[i], buffer); // Block auslesen
		
		int pos = 0; // Position im Block
		
		// Diese Schleife liest die eigentlichen Dir Tables aus
		for (;; ext_dir_num++) {
			ext_directorys[ext_dir_num] = (ext_linked_list_dir_t*) malloc (sizeof (ext_linked_list_dir_t));
			memcpy (ext_directorys[ext_dir_num], buffer+pos, sizeof (ext_linked_list_dir_t)); // Erstmal rüberkopieren
			
			if (ext_directorys[ext_dir_num]->inode == 0) {
				break;
			}
			
			pos += sizeof (ext_linked_list_dir_t)-4; // Die Position minus dem Pointer am Ende erhöhen
			ext_directorys[ext_dir_num]->name = (char*) malloc ((size_t)((uint8_t)ext_directorys[ext_dir_num]->name_len)+1); // Speicher für den Namen holen
			
			memset (ext_directorys[ext_dir_num]->name, 0x0, (int)((uint8_t)ext_directorys[ext_dir_num]->name_len)+1);
			memcpy (ext_directorys[ext_dir_num]->name, buffer+pos, (int)((uint8_t)ext_directorys[ext_dir_num]->name_len)); // Dateinamen rüber kopieren
			ext_directorys[ext_dir_num]->name[(int)((uint8_t)ext_directorys[ext_dir_num]->name_len)+1] = 0x0; // Laut Tyndur ist der Name NICHT null terminiert
			
			pos += (int)((uint8_t)ext_directorys[ext_dir_num]->name_len); // Nun noch die Länge des Namens addieren
			
			#ifdef DEBUG_READ_DIR_TABLES
			printf ("iNode: %u Name: %s ", ext_directorys[ext_dir_num]->inode, ext_directorys[ext_dir_num]->name);
			#endif
			
			//pos += (int)ext_directorys[ext_dir_num]->rec_len;
			
			
			if (pos % 4 > 0) {
				pos += 4-(pos % 4); // Und auf 4 ausrichten
			}
			
			
			// Wenn der Name leer ist, oder es die "." oder ".." Einträge sind, überspringen wir diesen Eintrag
			if (ext_directorys[ext_dir_num]->name[0] == 0x0 ||
				_strcmp (ext_directorys[ext_dir_num]->name, ".") ||
				_strcmp (ext_directorys[ext_dir_num]->name, "..")
			) {
				#ifdef DEBUG_READ_DIR_TABLES
				print ("Skipped\n");
				#endif
				
				free (ext_directorys[ext_dir_num]->name); // Daten wieder freigeben
				free (ext_directorys[ext_dir_num]);
				ext_dir_num--;
				
				continue;
			} else if ( ext_directorys[ext_dir_num]->file_type == EXT2_FT_DIR &&
				ext_inode_in_use (sb, bg, ext_directorys[ext_dir_num]->inode) &&
				!ext_find_inode (ext_directorys[ext_dir_num]->inode, ext_dir_num)
			) {
				
				ext_inode_table_t dir_inode;
				ext_read_inode (sb, bg, ext_directorys[ext_dir_num]->inode-1, &dir_inode);
				#ifdef DEBUG_READ_DIR_TABLES
				printf ("neuer Ordner (inode: %u)\n", ext_directorys[ext_dir_num]->inode);
				#endif
				//printf ("Eintrag %u ist ein Ordner (%s). iNode: %u Block: %u\n", ext_dir_num, ext_directorys[ext_dir_num]->name, ext_directorys[ext_dir_num]->inode, dir_inode.i_block[0]);
				
				struct fs_entry *folder = fs_add_node (ext_directorys[ext_dir_num]->name, FS_FLAG_FOLDER, parent, ext_drv);
				
				folder->userdata = ext_directorys[ext_dir_num]->inode-1;
				
				ext_dir_num++;
				ext_read_dir_tables (sb, bg, &dir_inode, folder); // Rekursiv aufrufen
				//ext_dir_num--;
			} else if ( ext_directorys[ext_dir_num]->file_type != EXT2_FT_DIR &&
				ext_directorys[ext_dir_num]->file_type != EXT2_FT_UNKNOWN &&
				!ext_find_inode (ext_directorys[ext_dir_num]->inode, ext_dir_num) /* &&
				(ext_directorys[ext_dir_num]->file_type >= EXT2_FT_REG_FILE && ext_directorys[ext_dir_num]->file_type <= EXT2_FT_SYMLINK)*/
			) {
				#ifdef DEBUG_READ_DIR_TABLES
				printf ("Datei (Parent: %s)\n", parent->name);
				#endif
				
				struct fs_entry *file = fs_add_node (ext_directorys[ext_dir_num]->name, 0x0, parent, ext_drv);
				file->userdata = ext_directorys[ext_dir_num]->inode-1; // Das UserData Feld enthält die iNode Nummer
				ext_dir_num++;
			}
			
			// Wenn kein weiterer Eintrag reinpasst abbrechen
			if (pos > ext_get_block_size (sb)/*-sizeof (ext_linked_list_dir_t)*/) {
				#ifdef DEBUG_READ_DIR_TABLES
				print ("next block\n");
				#endif
				
				break;
			}
		}
	}
	
	#ifdef DEBUG_READ_DIR_TABLES
	print ("done\n");
	#endif
	
	free (buffer);
	free (blocklist);
	return TRUE;
}

/* Startet den ext2 Treiber
 * Parameter: String auf ein Blockgerät
 * Rückgaben: Keine
 */
void ext_start (const char *device) {
	print ("Starte Ext2-Dateisystem Treiber... \n");
	
	cachenum = 0;
	
	ext_device = fopen (device, "rw");
	if (ext_device == NULL) {
		print ("Fehler\n");
		return;
	}
	
	ext_drv = fs_announce_driver (ext_init, ext_open, ext_close, ext_read, ext_write, ext_seek, ext_tell,
					NULL, NULL, NULL, NULL, NULL, NULL, NULL, ext_deinit);
	
	if (!ext_read_superblock ()) {
		print ("Kein Ext2 Geraet\n");
		return;
	}
	
	ext_read_block_group_desc (&ext_superblock, &ext_group_desc); // Diese Tabelle enthält die Blöcke, in denen die iNodes gespeichert sind
	ext_read_inode (&ext_superblock, &ext_group_desc, (uint32_t)1, &ext_dir_inode); // Der erste iNode zeigt auf die Directory Tables
	ext_read_dir_tables (&ext_superblock, &ext_group_desc, &ext_dir_inode, fs_get_first_entry()); // Directory Tables auslesen
	
	#if 0
	printf ("Gefundene Directory Tables: %u\n", ext_dir_num);
	
	char *filetypes[8] = { "Unbekannt", "Datei", "Ordner", "Zeichen Geraet", "Block Geraet", "Puffer Datei", "Socket", "SymLink" };
	
	uint32_t i;
	for (i=0; i<ext_dir_num; i++) {
		printf ("Eintrag %03u: '%s' (%s)\n", i, ext_directorys[i]->name, filetypes[ext_directorys[i]->file_type]);
	}
	#endif
	
	print ("OK\n");
}


/** VFS Treiber **/
struct ext_struct {
	char path[256];
	FILE *device; // FILE Handle auf das geöffnete Blockgerät
	uint32_t filelen;
	int handle;
	struct ext_struct *next;
};

struct ext_struct *ext_first_handle = NULL;
struct ext_struct *ext_handle_handle = NULL;
int ext_cur = 0;

BOOL ext_init (char nr) {
	return TRUE;
}


int ext_add_handle (struct ext_struct *handle) {
	handle->handle = ext_cur;
	ext_cur++;
	
	if (ext_first_handle == NULL) {
		ext_first_handle = handle;
		ext_handle_handle = handle;
		handle->next = NULL;
	} else {
		ext_handle_handle->next = handle;
		ext_handle_handle = handle;
		handle->next = NULL;
	}
	
	return ext_cur - 1;
}

void ext_del_handle (int handle_num) {
	struct ext_struct *ptr = ext_first_handle;
	
	if (ptr == NULL) {
		return;
	}
	
	if (ptr->next == NULL) { // Einziger offener Handle?
		free (ptr);
		ext_first_handle = ext_handle_handle = NULL;
	}
	
	while (ptr != NULL) { // Handle suchen
		if (ptr->next->handle == handle_num) {
			struct ext_struct *tmp = ptr->next;
			ptr->next = tmp->next;
			if (tmp == ext_handle_handle) {
				ext_handle_handle = ptr;
			}
			free (tmp);
			break;
		}
		
		ptr = ptr->next;
	}
}

struct ext_struct *ext_get_handle (int handle_num) {
	struct ext_struct *ptr = ext_first_handle;
	
	while (ptr != NULL) {
		if (ptr->handle == handle_num) {
			return ptr;
		}
		
		ptr = ptr->next;
	}
	
	return NULL;
}

BOOL ext_deinit () {
	fclose (ext_device);
	return TRUE;
}

FILE *ext_open (const char *path, struct fs_entry *entry, const char *params) {
	//printf ("ext_open (%s, %u, %s);\n", path, entry->userdata, params);
	
	// Gibts den iNode?
	if (!ext_inode_in_use (&ext_superblock, &ext_group_desc, entry->userdata)) {
		printf ("iNode nicht belegt!\n");
		return NULL;
	}
	
	
	ext_inode_table_t inode;
	ext_read_inode (&ext_superblock, &ext_group_desc, entry->userdata, &inode); // iNode laden
	
	//printf ("ext_open: filesize = %u block[0] = %u\n", inode.i_size, inode.i_block[0]);
	
	if (inode.i_mode & EXT2_FT_DIR) { // Wenns ein Ordner ist NULL returnen
		//printf ("Kann keinen Ordner oeffnen!\n");
		return NULL;
	}
	
	struct ext_struct *h = (struct ext_struct *) malloc (sizeof (struct ext_struct));
	FILE *f = (FILE*) malloc (sizeof (FILE));
	h->filelen = inode.i_size;
	strcpy (h->path, path);
	int num = ext_add_handle (h);
	f->driver = ext_drv;
	f->fpos = 0;
	f->handle = num;
	f->access = fs_parse_params (params);
	f->userdata = entry->userdata;
	
	// Nur schreibender Zugriff?
	if ( (f->access & FS_ACC_WRITE) && !(f->access & FS_ACC_READ) ) {
		h->filelen = 0;
	}
	
	return f;
}

size_t ext_read (FILE *handle, char *buffer, size_t bytes) {
	struct ext_struct *h = ext_get_handle (handle->handle);
	
	if (h == NULL) {
		return FS_ERR_EOF;
	}
	
	uint32_t block_size = ext_get_block_size (&ext_superblock);
	uint32_t block_index = (uint32_t)handle->fpos/block_size;
	ext_inode_table_t inode;
	
	char *buf = (char*) malloc (block_size);
	
	ext_read_inode (&ext_superblock, &ext_group_desc, handle->userdata, &inode); // iNode laden
	
	if (bytes > h->filelen-(uint32_t)handle->fpos) { // Wenn übers Dateiende hinaus gelesen werden soll einfach bytes auf
		bytes = h->filelen-(uint32_t)handle->fpos; // den höchst möglichen Wert setzen
	}
	
	#ifdef DEBUG_EXT_READ
	printf ("ext_read: Erstelle Blockliste... ");
	#endif
	
	uint32_t relpos = (uint32_t)handle->fpos % block_size; // Relative Position in einem Block
	uint32_t r;		// Zu lesende Bytes in einem Block
	uint32_t pos = 0; 	// Position im Buffer
	uint32_t ret = bytes; 	// Rückgabewert retten
	uint32_t *blocklist = ext_get_block_list (&ext_superblock, &inode); // Block liste erstellen
	
	#ifdef DEBUG_EXT_READ
	printf ("OK\n");
	#endif
	
	
	while (bytes > 0) {
		r = bytes;
		if (r > block_size-relpos) { // Wenn man mehr liest als einen Block
			r = block_size-relpos;
		}
		
		#ifdef DEBUG_EXT_READ
		printf ("ext_read_block: Durchgang: %u Bytes: %u (im block: %u) von block: %u", block_index, bytes, r, inode.i_block[block_index]);
		#endif
		
		ext_read_block (&ext_superblock, blocklist[block_index], buf); // Block einlesen
		
		#ifdef DEBUG_EXT_READ
		printf ("-\n");
		#endif
		
		memcpy (buffer+pos, buf+relpos, r);
		
		pos += r;
		bytes -= r;
		relpos = 0;
		block_index++;
	}
	
	free (blocklist);
	
	handle->fpos += ret;
	
	#ifdef DEBUG_EXT_READ
	print ("Done\n");
	#endif
	
	
	return ret;
}

size_t ext_write (FILE *handle, void *buffer, size_t bytes) {
	struct ext_struct *h = ext_get_handle (handle->handle);
	
	if (h == NULL) {
		return FS_ERR_EOF;
	}
	
	uint32_t block_size = ext_get_block_size (&ext_superblock);
	uint32_t block_index = (uint32_t)handle->fpos/block_size;

	ext_inode_table_t inode;
	
	char *buf = (char*) malloc (block_size);
	
	ext_read_inode (&ext_superblock, &ext_group_desc, handle->userdata, &inode); // iNode laden
	
	if ((uint32_t)handle->fpos+bytes > h->filelen) { // Wird die Datei größer?
		h->filelen += (h->filelen - (uint32_t)handle->fpos+bytes); 
	}
	
	uint32_t relpos = (uint32_t)handle->fpos % block_size; // Relative Position in einem Block
	uint32_t r;		// Zu lesende Bytes in einem Block
	uint32_t pos = 0; 	// Position im Buffer
	uint32_t ret = bytes; 	// Rückgabewert retten
	uint32_t *blocklist = ext_get_block_list (&ext_superblock, &inode); // Block liste erstellen
	
	while (bytes > 0) {
		
		//printf ("write: Durchgang: %u Bytes: %u Block: %u\n", block_index, bytes, blocklist[block_index]);
		if (blocklist[block_index] == 0) { // Wenn das passiert müssen wir einen neuen freien Block finden
			blocklist[block_index] = ext_find_free_block (&ext_superblock, &ext_group_desc);
			
			if (blocklist[block_index] == 0) { // Es gibt keinen freien Block mehr
				return FALSE;
			}
		}
		
		r = bytes;
		if (r > block_size-relpos) { // Wenn man mehr liest als einen Block
			r = block_size-relpos;
		}
		
		ext_read_block (&ext_superblock, blocklist[block_index], buf);
		memcpy (buf+relpos, buffer+pos, r);
		ext_write_block (&ext_superblock, blocklist[block_index], buf); // Block schreiben
		
		//memcpy (buffer+pos, buf+relpos, r);
		
		pos += r;
		bytes -= r;
		relpos = 0;
		block_index++;
	}
	
	inode.i_blocks = h->filelen / block_size;
	if (h->filelen % block_size > 0) {
		inode.i_blocks++;
	}
	
	if(!ext_update_inode (&ext_superblock, &ext_group_desc, handle->userdata, &inode, blocklist, (uint32_t)h->filelen, TRUE, TRUE, FALSE)) {
		free (blocklist);
		handle->fpos += ret;
		return 0;
	}
	
	free (blocklist);
	handle->fpos += ret;
	
	return ret;
}

int ext_close (FILE *handle) {
	ext_del_handle (handle->handle);
	return FS_ERR_SUCCESS;
}

int ext_seek (FILE *handle, int offset, int origin) {
	struct ext_struct *h = ext_get_handle (handle->handle);
	int pos = handle->fpos;
	
	switch (origin) {
		case SEEK_SET:
			pos = offset;
			break;
		case SEEK_CUR:
			pos += offset;
			break;
		case SEEK_END:
			pos = h->filelen + offset;
			break;
	}
	
	if (pos < 0) { // Bei negativen Werten ist die Position relativ zum Dateiende
		int sub = (pos*-1) % h->filelen;
		pos = h->filelen - sub;
	}
	
	if (pos > h->filelen) {
		return 1; // Fehler
	}
	
	handle->fpos = pos;
	
	return 0;
}

size_t ext_tell (FILE *handle) {
	return handle->fpos;
}
